﻿using System;
using sharpxbot.Core.Connection;
using sharpxbot.Core.Security.Traffic.Counters;
using sharpxbot.Core.Security.Traffic.Reports;

namespace sharpxbot.Core.Security.Traffic
{
    public class TrafficRegistry : ITrafficRegistry, ITrafficReportGenerator
    {
        public TrafficRegistry(IConnection connection, int trafficLimit)
        {
            if (connection == null)
                throw new NullReferenceException("Illegal null-reference IConnection. TrafficRegistry ctor");

            this.connection = connection;
            this.TrafficLimit = trafficLimit;

            connection.OnSocketRead += NotifyIncoming;
            connection.OnSocketWrite += NotifyOutgoing;

            incomingCounters = new YearCounters();
            outgoingCounters = new YearCounters();

            connection.OnSocketRead += NotifyIncoming;
            connection.OnSocketWrite += NotifyOutgoing;
        }

        #region ITrafficRepository Members

        public int TrafficLimit { get; private set; }

        public TrafficLimitExceededEventHandler OnLimitExceeded { get; set; }

        public void NotifyIncoming(int bytesCount)
        {
            Notify(bytesCount, incomingCounters);
        }

        public void NotifyOutgoing(int bytesCount)
        {
            Notify(bytesCount, outgoingCounters);
        }

        #endregion

        #region ITrafficReportGenerator Members

        public ITrafficReport GetReport(int year, int month, int day, int hour)
        {
            return
                new HourReport(incomingCounters.Months[month - 1].Days[day - 1].Hours[hour], 
                    outgoingCounters.Months[month - 1].Days[day - 1].Hours[hour]);
        }

        public ITrafficReport GetReport(int year, int month, int day)
        {
            return 
                new DayReport(incomingCounters.Months[month - 1].Days[day - 1], outgoingCounters.Months[month - 1].Days[day - 1]);
        }

        #endregion

        #region Private methods

        private void Notify(int bytesCount, YearCounters counters)
        {
            if (counters == null)
                return;

            DateTime now = DateTime.Now;

            MonthCounters month = counters.Months[now.Month - 1];
            DayCounters day = month.Days[now.Day - 1];
            HourCounters hour = day.Hours[now.Hour];
            hour.Minutes[now.Minute] += bytesCount;

            long currentValue = hour.Minutes[now.Minute];

            ValidateTrafficLimitExceeded(currentValue);
        }

        private void ValidateTrafficLimitExceeded(long currentValue)
        {
            if (currentValue > TrafficLimit && OnLimitExceeded != null)
            {
                TrafficLimitExceededEventArgs e = new TrafficLimitExceededEventArgs()
                {
                    CurrentValue = currentValue,
                    LimitValue = TrafficLimit,
                    Timestamp = DateTime.Now
                };

                OnLimitExceeded(this, e);
            }
        }

        #endregion

        #region Private fields

        private IConnection connection;

        private YearCounters incomingCounters;
        private YearCounters outgoingCounters;

        #endregion
    }
}
